using System;

namespace WindowsAPI
{
    /// <summary>
    /// The NamedEvent class is to suppliment .NET's event classes with the ability to span
    /// processes in both .NET and Win32 with the same event signaling capability.
    /// 
    /// The NamedEvent class object provides the ability to wait on, pulse, set, and
    /// reset a named event. The event can be created with manual or automatic reset,
    /// and is always created with an initial state of reset(false).
    /// 
    /// The NamedEvent class will not leave a handle open past any method
    /// call, so garbage collection is irrelevant.
    /// 
    /// The NamedEvent class defines both instance methods and static methods.
    /// The static methods require a name, where as the instance methods contain the
    /// name in the object instance already. The static methods are also limited to
    /// the configuration of auto reset and initially not signaled.
    /// 
    /// The wait methods will wait on a single object, the named event, only. There
    /// is no multiple event support.
    ///
    /// Win32 security is a critical issue. This class does not support specifically
    /// identifying any security, other than the default process security context.
    /// This class is best suitable for processes run in the same security context,
    /// such as the desktop's interactive user account.
    /// </summary>
    public class NamedEvent
    {
        private readonly string f_EventName;
        private IntPtr f_Handle;
        private readonly IntPtr f_Attributes = IntPtr.Zero;
        private readonly bool f_ManualReset;
        private readonly bool f_InitialState;

        /// <summary>Create a NamedEvent object with the name of the event, and assume auto reset with an initial state of reset.</summary>
        public NamedEvent(string EventName) : this(EventName, false) { }

        /// <summary>Create a NamedEvent object with the name of the event and the auto reset property, assuming an initial state of reset.</summary>
        public NamedEvent(string EventName, bool ManualReset)
        {
            f_EventName = EventName;
            f_ManualReset = ManualReset;
            f_InitialState = false;
        }

        /// <summary>
        /// Wait for the event to signal to a maximum period of TimeoutInSecs total seconds.
        /// Returns true if the event signaled, false if timeout occurred.
        /// </summary>
        public bool Wait(int TimeoutInSecs)
        {
            f_Handle = Kernel32.Events.CreateEvent(f_Attributes, f_ManualReset, f_InitialState, f_EventName);
            var rc = Kernel32.Events.WaitForSingleObject(f_Handle, TimeoutInSecs * 1000);
            Kernel32.CloseHandle(f_Handle);
            return rc == 0;
        }

        /// <summary>Pulse the named event, which results in a single waiting thread to exit the Wait method.</summary>
        public bool Pulse()
        {
            f_Handle = Kernel32.Events.CreateEvent(f_Attributes, f_ManualReset, f_InitialState, f_EventName);
            Kernel32.Events.PulseEvent(f_Handle);
            Kernel32.CloseHandle(f_Handle);
            return f_Handle != IntPtr.Zero;
        }

        /// <summary>
        /// Set the named event to a signaled state. The Wait() method will not block any
        /// thread as long as the event is in a signaled state.
        /// </summary>
        public void Set()
        {
            f_Handle = Kernel32.Events.CreateEvent(f_Attributes, f_ManualReset, f_InitialState, f_EventName);
            Kernel32.Events.SetEvent(f_Handle);
            Kernel32.CloseHandle(f_Handle);
        }

        /// <summary>
        /// Reset the named event to a non signaled state. The Wait() method will block
        /// any thread that enters it as long as the event is in a non signaled state.
        /// </summary>
        public void Reset()
        {
            f_Handle = Kernel32.Events.CreateEvent(f_Attributes, f_ManualReset, f_InitialState, f_EventName);
            Kernel32.Events.ResetEvent(f_Handle);
            Kernel32.CloseHandle(f_Handle);
        }


        /// <summary>
        /// Wait for the event with the given name to signal to a maximum period of TimeoutInSecs total seconds.
        /// Returns true if the event signaled, false if timeout occurred.
        /// </summary>
        static public bool Wait(int TimeoutInSecs, string Name) { return (new NamedEvent(Name)).Wait(TimeoutInSecs); }

        /// <summary>Pulse the event with the given name, which results in a single waiting thread to exit the Wait method.</summary>
        static public bool Pulse(string Name) { return (new NamedEvent(Name)).Pulse(); }

        /// <summary>
        /// Set the event with the given name to a signaled state. The Wait() method will not block
        /// any threads as long as the event is in a signaled state.
        /// </summary>
        static public void Set(string Name) { (new NamedEvent(Name)).Set(); }

        /// <summary>
        /// Reset the event with the given name to a non signaled state. The Wait() method will block
        /// any thread that enters it as long as the event is in a non signaled state.
        /// </summary>
        static public void Reset(string Name) { (new NamedEvent(Name)).Reset(); }
    }
}